/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab-modules.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

//===========================================================================================
#include "Ogre_Gfx.h"

//===========================================================================================
dylab::Ogre_Gfx::Object::Object(const String & _name, IScene * _scene)
: mUserData(NULL)
{
    // check arguments
    DYLAB_ASSERT(_scene != NULL);

    Ogre::SceneManager * ogreSceneMgr = dynamicCast<Scene>(_scene)->getOgreSceneMgr();

    mOgreManualObject = ogreSceneMgr->createManualObject(_name);
    DYLAB_ASSERT(mOgreManualObject != NULL);

    // attach object to scene node
    ogreSceneMgr->getRootSceneNode()->createChildSceneNode(_name)->attachObject(mOgreManualObject);

    // store our object
    mOgreManualObject->setUserObject(this);
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::Object::~Object()
{
}
//-----------------------------------------------------------------------------
dylab::String dylab::Ogre_Gfx::Object::getName() const
{
    return mOgreManualObject->getName();
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Object::setDynamic(bool _dynamic)
{
    mOgreManualObject->setDynamic(_dynamic);
}
//-----------------------------------------------------------------------------
bool dylab::Ogre_Gfx::Object::isDynamic() const
{
    return mOgreManualObject->getDynamic();
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Object::createSection(const String & _material, section_t _sectionType,
                        const PositionBuffer & _positions, 
                        const IndexBuffer & _indices,
                        const NormalBuffer * _normals,
                        const TexCoordBuffer * _texCoords,
                        const ColourBuffer * _colours)
{    
    // enter our estimations
    mOgreManualObject->estimateVertexCount(_positions.getCount());
    mOgreManualObject->estimateIndexCount(_indices.getCount());

    Ogre::RenderOperation::OperationType op;
    switch (_sectionType)
    {
    case ST_TRIANGLE_LIST:
        op = Ogre::RenderOperation::OT_TRIANGLE_LIST;
        break;
    case ST_TRIANGLE_FAN:
        op = Ogre::RenderOperation::OT_TRIANGLE_FAN;
        break;
    case ST_TRIANGLE_STRIP:
        op = Ogre::RenderOperation::OT_TRIANGLE_STRIP;
        break;
    case ST_LINE_LIST:
        op = Ogre::RenderOperation::OT_LINE_LIST;
        break;
    case ST_LINE_STRIP:
        op = Ogre::RenderOperation::OT_LINE_STRIP;
        break;
    case ST_POINT_LIST:
        op = Ogre::RenderOperation::OT_POINT_LIST;
        break;
    }

    // begin generating the object
    mOgreManualObject->begin(_material, op);

    // generate the object
    _generate(_positions, _indices, _normals, _texCoords, _colours);

    // object generated
    mOgreManualObject->end();
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Object::updateSection(uint32_t _section,
                        const PositionBuffer & _positions, 
                        const IndexBuffer & _indices,
                        const NormalBuffer * _normals,
                        const TexCoordBuffer * _texCoords,
                        const ColourBuffer * _colours)
{
    DYLAB_ASSERT(_section < mOgreManualObject->getNumSections());

    // enter our estimations
    mOgreManualObject->estimateVertexCount(_positions.getCount());
    mOgreManualObject->estimateIndexCount(_indices.getCount());

    // begin generating the object
    mOgreManualObject->beginUpdate(_section);

    // generate the object
    _generate(_positions, _indices, _normals, _texCoords, _colours);

    // object generated
    mOgreManualObject->end();
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Object::changeSectionMaterialName(uint32_t _section, const String & _name)
{
    DYLAB_ASSERT(_section < mOgreManualObject->getNumSections());
    mOgreManualObject->setMaterialName(_section, _name);
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Object::setRayQueryFlags(uint32_t _flags)
{
    // set the ray scene query flags
    mOgreManualObject->setQueryFlags(_flags);
}
//-----------------------------------------------------------------------------
dylab::uint32_t dylab::Ogre_Gfx::Object::getRayQueryFlags() const
{
    return mOgreManualObject->getQueryFlags();
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Object::setCastShadows(bool _castShadows)
{
    mOgreManualObject->setCastShadows(_castShadows);
}
//-----------------------------------------------------------------------------
bool dylab::Ogre_Gfx::Object::isCastingShadows() const
{
    return mOgreManualObject->getCastShadows();
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Object::translate(const Vector3 & _v)
{
    mOgreManualObject->getParentSceneNode()->translate(toOgreVector3(_v));
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Object::rotate(const Vector3 & _axis, real_t _degrees)
{
    mOgreManualObject->getParentSceneNode()->rotate(toOgreVector3(_axis), Ogre::Degree(_degrees));
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Object::scale(const Vector3 & _scale)
{
    mOgreManualObject->getParentSceneNode()->scale(toOgreVector3(_scale));
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Object::setUserData(IUserData * _userData)
{
    mUserData = _userData;
}
//-----------------------------------------------------------------------------
dylab::Ogre_Gfx::Object::IUserData * dylab::Ogre_Gfx::Object::getUserData()
{
    return mUserData;
}
//-----------------------------------------------------------------------------
void dylab::Ogre_Gfx::Object::_generate(const PositionBuffer & _positions, const IndexBuffer & _indices, const NormalBuffer * _normals,
                                                       const TexCoordBuffer * _texCoords, const ColourBuffer * _colours)
{
    uint32_t vertexCount = _positions.getCount();
    uint32_t indexCount = _indices.getCount();

    // check the buffer sizes
    if (_normals != NULL && _normals->getCount() != vertexCount)
        throw EInvalidArgument(
			String::fmt("Normals vector buffer (%1%) size should have the same size as position buffer (%1%)", 
				_normals->getCount(), vertexCount));

    if (_texCoords != NULL && _texCoords->getCount() != vertexCount)
        throw EInvalidArgument(
			String::fmt("Tex. coords buffer (%1%) size should have the same size as position buffer (%1%)", 
				_texCoords->getCount(), vertexCount));

    if (_colours != NULL && _colours->getCount() != vertexCount)
        throw EInvalidArgument(
			String::fmt("Colours buffer (%1%) size should have the same size as position buffer (%1%)", 
				_colours->getCount(), vertexCount));

    // object axis aligned bounding box
    Ogre::AxisAlignedBox axisAlignedBox;

    // empty fake containers
    NormalBuffer fakeNormalBuffer;
    TexCoordBuffer fakeTexCoordBuffer;
    ColourBuffer fakeColourBuffer;

    // first, generate the vertices
    NormalBuffer::ConstIterator normal(_normals != NULL ? *_normals : fakeNormalBuffer);
    TexCoordBuffer::ConstIterator texCoord(_texCoords != NULL ? *_texCoords : fakeTexCoordBuffer);
    ColourBuffer::ConstIterator colour(_colours != NULL ? *_colours : fakeColourBuffer);
    for (PositionBuffer::ConstIterator position(_positions); position.isValid(); position++)
    {
        Ogre::Vector3 pos = toOgreVector3(position());
        mOgreManualObject->position(pos);
        axisAlignedBox.merge(pos);

        if (_normals != NULL)
        {
            mOgreManualObject->normal(toOgreVector3(normal()));
            normal++;
        }
        if (_texCoords != NULL)
        {
            mOgreManualObject->textureCoord(texCoord->u, texCoord->v);
            texCoord++;
        }
        if (_colours != NULL)
        {
            mOgreManualObject->colour(toOgreColour(colour()));
            colour++;
        }
    }

    // overshoot a little
    //TODO: use class attribute
    Ogre::Vector3 v = axisAlignedBox.getMaximum() - axisAlignedBox.getMinimum();
    v.normalise();
    axisAlignedBox.setMinimum(axisAlignedBox.getMinimum() - v);
    axisAlignedBox.setMaximum(axisAlignedBox.getMaximum() + v); 

    // updated object's axis aligned box and the bounding sphere
    mOgreManualObject->setBoundingBox(axisAlignedBox);
    
    // second, add the indices
    for (IndexBuffer::ConstIterator index(_indices); index.isValid(); index++)
    {
        mOgreManualObject->index(index());
    }
}
//-----------------------------------------------------------------------------